=begin pod :kind("Type") :subkind("class") :category("domain-specific")

=TITLE class IO::Spec::Unix

=SUBTITLE Platform specific operations on file and directory paths for POSIX

    class IO::Spec::Unix is IO::Spec  { }

An object of this type is available via the variable C<$*SPEC> if the
Raku interpreter is running on a Unix-like platform.

About this class and its related classes also see
L<IO::Spec|/type/IO::Spec>.

=head1 Methods

=head2 method abs2rel

Defined as:

    method abs2rel(IO::Path:D $path, IO::Path:D $base = $*CWD --> Str:D)

Returns a string that represents C<$path>, but relative to C<$base>
path. Both C<$path> and C<$base> may be relative paths. C<$base>
defaults to C<$*CWD>.

=head2 method basename

Defined as:

    method basename(Str:D $path --> Str:D)

Takes a path as a string and returns a possibly-empty portion after the
last slash:

    IO::Spec::Unix.basename("foo/bar/") .raku.say; # OUTPUT: «""␤»
    IO::Spec::Unix.basename("foo/bar/.").raku.say; # OUTPUT: «"."␤»
    IO::Spec::Unix.basename("foo/bar")  .raku.say; # OUTPUT: «"bar"␤»

=head2 method canonpath

Defined as:

    method canonpath(Str() $path, :$parent --> Str:D)

Returns a string that is a canonical representation of C<$path>. If
C<:$parent> is set to true, will also clean up references to parent
directories. B<NOTE:> the routine does not access the filesystem, so no
symlinks are followed.

    IO::Spec::Unix.canonpath("foo//../bar/../ber").say;
    # OUTPUT: «foo/../bar/../ber␤»

    IO::Spec::Unix.canonpath("foo///./../bar/../ber").say;
    # OUTPUT: «foo/../bar/../ber␤»

    IO::Spec::Unix.canonpath("foo///./../bar/../ber", :parent).say;
    # OUTPUT: «ber␤»

=head2 method catdir

Defined as:

    method catdir (*@parts --> Str:D)

Concatenates multiple path fragments and returns the canonical
representation of the resultant path as a string. The C<@parts> are
L«C<Str>|/type/Str» objects and are allowed to contain path separators.

    IO::Spec::Unix.catdir(<foo/bar ber raku>).say; # OUTPUT: «foo/bar/ber/raku␤»

=head2 method catfile

Alias for L«C<catdir>|/routine/catdir».

=head2 method catpath

Defined as:

    method catpath ($, Str:D $part1, Str:D $part2 --> Str:D)

Takes two path fragments and concatenates them, adding or removing a
path separator, if necessary. The first argument is ignored (it exists
to maintain consistent interface with other L«C<IO::Spec>|/type/IO::Spec» types for
systems that have volumes).

    IO::Spec::Unix.catpath($, 'some/dir', 'and/more').say;
    # OUTPUT: «some/dir/and/more␤»

=head2 method curdir

Defined as:

    method curdir()

Returns a string representing the current directory:

    say '.' eq $*SPEC.curdir; # OUTPUT: «True␤»

=head2 method curupdir

Defined as:

    method curupdir()

Returns a L«C<Block>|/type/Block» taking an argument. This block
returns C<True> if its argument is neither the string representing the
current directory nor the string representing the directory one up from
the current one.  It returns C<False> otherwise.
This block is intended to be used with
L<smartmatching|/language/operators#infix_~~>.

=begin code
say $*SPEC.curupdir;
# OUTPUT: «-> str $dir { #`(Block|65335808) ... }␤»

my @dirs = <. foo .. bar>;
say @dirs.grep: { $_ ~~ $*SPEC.curupdir };
# OUTPUT: «(foo bar)␤»
=end code

Neither C<foo> nor C<bar> are equal to the representation of the current
or parent directory, that is why they are returned by C<grep>.

B<Note>: Before Rakudo version 2020.06 a L«C<none>|/routine/none»
L<Junction|/type/Junction> was returned instead of a C<Block>.

=head2 method devnull

Defined as:

    method devnull(--> Str:D)

Returns the string C<"/dev/null"> representing the
L<"Null device"|https://en.wikipedia.org/wiki/Null_device>:

=for code
$*SPEC.devnull.IO.spurt: "foo bar baz";

=head2 method dir-sep

Defined as:

    method dir-sep(--> Str:D)

Returns the string C<"/"> representing canonical directory separator
character.

=for code
IO::Spec::Unix.dir-sep.say; # OUTPUT: «/␤»

=head2 method extension

B<NOTE:> Most users would want to use the higher-level routine
L«C<IO::Path.extension>|/type/IO::Path#method_extension» instead of this
lower-level version.

Defined as:

    method extension(Str:D $path --> Str:D)

Takes a string representing a base name and returns the characters after
the last dot (C<".">), or empty string if no dots are present. The
routine makes no attempt to detect path separators and will return
everything after the last dot.

    $*SPEC.extension('foo.'      ).raku.say;  # OUTPUT: «""␤»
    $*SPEC.extension('foo.txt'   ).raku.say;  # OUTPUT: «"txt"␤»
    $*SPEC.extension('foo.tar.gz').raku.say;  # OUTPUT: «"gz"␤»
    $*SPEC.extension('foo'       ).raku.say;  # OUTPUT: «""␤»
    $*SPEC.extension('bar.foo/foo').raku.say; # OUTPUT: «"foo/foo"␤»

=head2 method is-absolute

Defined as:

    method is-absolute(Str:D $path --> Bool:D)

Returns C<True> if the C<$path> starts with a slash (C<"/">), even if it
has combining character on it:

    say IO::Spec::Unix.is-absolute: "/foo";        # OUTPUT: «True␤»
    say IO::Spec::Unix.is-absolute: "/\x[308]foo"; # OUTPUT: «True␤»
    say IO::Spec::Unix.is-absolute: "bar";         # OUTPUT: «False␤»

=head2 method join

Defined as:

    method join ($, Str:D $dir, Str:D $file --> Str:D)

Similar to L«C<catpath>|/routine/catpath», takes two path fragments and
concatenates them, adding or removing a path separator, if necessary,
except it will return just C<$file> if both C<$dir> and C<$file> are
string C<'/'> or if C<$dir> is the string C<'.'>. The first argument is
ignored (it exists to maintain consistent interface with other
C<IO::Spec> types for systems that have volumes).

    IO::Spec::Unix.join($, 'foo', 'bar').say; # OUTPUT: «foo/bar␤»
    IO::Spec::Unix.join($, '/', '/').say;     # OUTPUT: «/␤»
    IO::Spec::Unix.join($, '.', 'foo').say;   # OUTPUT: «foo␤»
    say $*SPEC.join(True,".","/foo");         # OUTPUT: «/foo␤»

=head2 method path

Defined as:

    method path(--> Seq:D)

Splits the value of C«%*ENV<PATH>» on colons (C<":">), replaces empty parts with
C<".">, and returns a L<Seq|/type/Seq> with each of the resultant parts. Returns
an empty L<Seq|/type/Seq> if C«%*ENV<PATH>» is not set or is an empty string.

    %*ENV<PATH> = 'foo:bar/ber::foo:';
    IO::Spec::Unix.path.raku.say;
    # OUTPUT: «("foo", "bar/ber", ".", "foo", ".").Seq␤»

=head2 method rel2abs

Defined as:

    method rel2abs(Str() $path, $base = $*CWD --> Str:D)

Returns a string representing C<$path> converted to absolute path, based at
C<$base>, which defaults to C<$*CWD>. If C<$base> is not an absolute path,
it will be made absolute relative to C<$*CWD>, unless C<$*CWD> and C<$base>
are the same.

=begin code
say $*CWD;                                  # OUTPUT: «"/home/camelia".IO␤»

say IO::Spec::Unix.rel2abs: 'foo';          # OUTPUT: «/home/camelia/foo␤»
say IO::Spec::Unix.rel2abs: './';           # OUTPUT: «/home/camelia␤»
say IO::Spec::Unix.rel2abs: 'foo/../../';   # OUTPUT: «/home/camelia/foo/../..␤»
say IO::Spec::Unix.rel2abs: '/foo/';        # OUTPUT: «/foo␤»

say IO::Spec::Unix.rel2abs: 'foo', 'bar';   # OUTPUT: «/home/camelia/bar/foo␤»
say IO::Spec::Unix.rel2abs: './', '/bar';   # OUTPUT: «/bar␤»
say IO::Spec::Unix.rel2abs: '/foo/', 'bar'; # OUTPUT: «/foo␤»

say IO::Spec::Unix.rel2abs: 'foo/../../', 'bar';
# OUTPUT: «/home/camelia/bar/foo/../..␤»
=end code

=head2 method rootdir

Defined as:

    method rootdir(--> Str:D)

Returns string C<'/'>, representing root directory.

=head2 method split

Defined as:

    method split(IO::Spec::Unix: Cool:D $path)

Creates a L«C<IO::Path::Parts>|/type/IO::Path::Parts» for C<$path>,
with an empty string as its C<volume> attribute's value.

=begin code
IO::Spec::Unix.split('C:/foo/bar.txt').raku.say;
# OUTPUT: «IO::Path::Parts.new("","C:/foo","bar.txt")␤»

IO::Spec::Unix.split('/foo/').raku.say;
# OUTPUT: «IO::Path::Parts.new("","/","foo")␤»

IO::Spec::Unix.split('///').raku.say;
# OUTPUT: «IO::Path::Parts.new("","/","/")␤»

IO::Spec::Unix.split('./').raku.say;
# OUTPUT: «IO::Path::Parts.new("",".",".")␤»

IO::Spec::Unix.split('.').raku.say;
# OUTPUT: «IO::Path::Parts.new("",".",".")␤»

IO::Spec::Unix.split('').raku.say;
# OUTPUT: «IO::Path::Parts.new("","","")␤»
=end code

B<Note>: Before Rakudo version 2020.06 this method split the given
C<$path> into "volume", "dirname", and "basename" and returned the result
as a L<List|/type/List> of three L<Pairs|/type/Pair>, in that order.

=head2 method splitdir

Defined as:

    method splitdir(Cool:D $path --> List:D)

Splits the given C<$path> on slashes.

=begin code
IO::Spec::Unix.splitdir('C:\foo/bar.txt').raku.say;
# OUTPUT: «("C:\\foo", "bar.txt")␤»

IO::Spec::Unix.splitdir('/foo/').raku.say;
# OUTPUT: «("", "foo", "")␤»

IO::Spec::Unix.splitdir('///').raku.say;
# OUTPUT: «("", "", "", "")␤»

IO::Spec::Unix.splitdir('./').raku.say;
# OUTPUT: «(".", "")␤»

IO::Spec::Unix.splitdir('.').raku.say;
# OUTPUT: «(".",)␤»

IO::Spec::Unix.splitdir('').raku.say;
# OUTPUT: «("",)␤»
=end code

=head2 method splitpath

Defined as:

    method splitpath(Cool:D $path, :$nofile --> List:D)

Splits the given C<$path> into a list of 3 strings: volume,
dirname, and file. The volume is always an empty string, returned for API
compatibility with other L<IO::Spec|/type/IO::Spec> types. If C<:$nofile> named argument is
set to C<True>, the content of the file string is undefined and should be
ignored; this is a means to get a performance boost, as implementations may use
faster code path when file is not needed.

=begin code
IO::Spec::Unix.splitpath('C:\foo/bar.txt').raku.say;
# OUTPUT: «("", "C:\\foo/", "bar.txt")␤»

IO::Spec::Unix.splitpath('C:\foo/bar.txt', :nofile).raku.say;
# OUTPUT: «("", "C:\\foo/bar.txt", "")␤»

IO::Spec::Unix.splitpath('/foo/').raku.say;
# OUTPUT: «("", "/foo/", "")␤»

IO::Spec::Unix.splitpath('/foo/', :nofile).raku.say;
# OUTPUT: «("", "/foo/", "")␤»

IO::Spec::Unix.splitpath('///').raku.say;
# OUTPUT: «("", "///", "")␤»

IO::Spec::Unix.splitpath('./').raku.say;
# OUTPUT: «("", "./", "")␤»

IO::Spec::Unix.splitpath('.').raku.say;
# OUTPUT: «("", "", ".")␤»

IO::Spec::Unix.splitpath('').raku.say;
# OUTPUT: «("", "", "")␤»
=end code

=head2 method tmpdir

Defined as:

        method tmpdir(--> IO::Path:D)

Attempts to locate a system's temporary directory by checking several typical directories and environmental variables. Uses current directory if no suitable directories are found.

=head2 method updir

Defined as:

    method updir()

Returns a string representing the directory one up from current:

    say '..' eq $*SPEC.updir; # OUTPUT: «True␤»

=end pod

# vim: expandtab shiftwidth=4 ft=perl6
